import {
  Attachments,
  Bubble,
  Conversations,
  Prompts,
  Sender,
  Welcome,
  useXAgent,
  useXChat,
  XStream,
} from "@ant-design/x";
import type { BubbleProps } from "@ant-design/x";
import React, { useEffect, useState, useRef } from "react";
import {
  CloudUploadOutlined,
  CommentOutlined,
  FireOutlined,
  HeartOutlined,
  MenuFoldOutlined,
  MenuUnfoldOutlined,
  PaperClipOutlined,
  PlusOutlined,
  ReadOutlined,
  SmileOutlined,
  UserOutlined,
  OpenAIOutlined,
  CloseOutlined,
  LogoutOutlined,
  CaretDownOutlined,
  SettingOutlined,
} from "@ant-design/icons";
import {
  Badge,
  Button,
  type GetProp,
  Space,
  Spin,
  Flex,
  Typography,
  message,
} from "antd";
import PdfOreviewer from "./components/PdfPreviewer";
import markdownit from "markdown-it";
import axios from "axios";
import { fetchEventSource } from "@microsoft/fetch-event-source";
import styles from "./index.module.less";
import { useNavigate } from "react-router-dom";
import { getSession } from "../../utils/storage";
import { authService } from "../../services/auth";

const md = markdownit({ html: true, breaks: true });

const renderTitle = (icon: React.ReactElement, title: string) => (
  <Space align="start">
    {icon}
    <span>{title}</span>
  </Space>
);

const defaultConversationsItems = [
  {
    key: "0",
    label: "未来一年白酒股行情如何?",
  },
];

const placeholderPromptsItems: GetProp<typeof Prompts, "items"> = [
  {
    key: "1",
    label: renderTitle(<FireOutlined style={{ color: "#FF4D4F" }} />, "热点"),
    description: "你对什么感兴趣？",
    children: [
      {
        key: "1-1",
        description: `白酒板块表现如何？`,
      },
      {
        key: "1-2",
        description: `CPI指数和白酒销量什么关系？`,
      },
      {
        key: "1-3",
        description: `PPI和白酒销量什么关系呢？`,
      },
    ],
  },
  {
    key: "2",
    label: renderTitle(<ReadOutlined style={{ color: "#1890FF" }} />, "指引"),
    description: "你想知道哪个行业?",
    children: [
      {
        key: "2-1",
        icon: <HeartOutlined />,
        description: `白酒行业`,
      },
      {
        key: "2-2",
        icon: <SmileOutlined />,
        description: `金融行业`,
      },
      {
        key: "2-3",
        icon: <CommentOutlined />,
        description: `房地产行业`,
      },
    ],
  },
];

const senderPromptsItems: GetProp<typeof Prompts, "items"> = [
  {
    key: "1",
    description: "热点",
    icon: <FireOutlined style={{ color: "#FF4D4F" }} />,
  },
  {
    key: "2",
    description: "指引",
    icon: <ReadOutlined style={{ color: "#1890FF" }} />,
  },
];
const renderMarkdown: BubbleProps["messageRender"] = (content) => {
  // 使用正则表达式匹配[^n^]模式
  const regex = /\[\^(\d+)\^\]/g;

  // 替换函数
  const result = content.replace(regex, (match, number) => {
    number++;
    return `<span class="answer_source_tag" tagnumber="${number}" tagindex="${number}">${number}</span>`;
  });

  return (
    <Typography>
      {/* biome-ignore lint/security/noDangerouslySetInnerHtml: used in demo */}
      <div dangerouslySetInnerHTML={{ __html: md.render(result) }} />
    </Typography>
  );
};

const roles: GetProp<typeof Bubble.List, "roles"> = {
  ai: {
    placement: "start",
    avatar: { icon: <OpenAIOutlined />, style: { background: "#fde3cf" } },
    typing: { step: 5, interval: 20 },
    style: {
      maxWidth: 600,
    },
    loadingRender: () => (
      <Space>
        <Spin size="small" />
        Custom loading...
      </Space>
    ),
    messageRender: (content) => (
      <Typography>
        {/* biome-ignore lint/security/noDangerouslySetInnerHtml: used in demo */}
        <div dangerouslySetInnerHTML={{ __html: md.render(content) }} />
      </Typography>
    ),
  },
  user: {
    placement: "end",
    avatar: { icon: <UserOutlined />, style: { background: "#87d068" } },
  },

  suggestion: {
    placement: "start",
    avatar: { icon: <UserOutlined />, style: { visibility: "hidden" } },
    variant: "borderless",
    messageRender: (items) => <Prompts vertical items={items as any} />,
  },
  file: {
    placement: "start",
    avatar: { icon: <UserOutlined />, style: { visibility: "hidden" } },
    variant: "borderless",
    messageRender: (items: any) => (
      <Flex vertical gap="middle">
        {(items as any[]).map((item) => (
          <Attachments.FileCard key={item.uid} item={item} />
        ))}
      </Flex>
    ),
  },
};

const ChatPage: React.FC = () => {
  // ==================== State ====================
  const [headerOpen, setHeaderOpen] = React.useState(false);
  const [menuCollapsed, setMenuCollapsed] = useState(false);
  const [content, setContent] = React.useState("");

  const [conversationsItems, setConversationsItems] = React.useState(
    defaultConversationsItems
  );

  const [activeKey, setActiveKey] = React.useState(
    defaultConversationsItems[0].key
  );

  const [attachedFiles, setAttachedFiles] = React.useState<
    GetProp<typeof Attachments, "items">
  >([]);

  const [lines, setLines] = React.useState<Record<string, string>[]>([]);

  const [showPdf, setShowPdf] = React.useState(true);

  // 添加用户信息状态
  const [userInfo, setUserInfo] = useState({
    username: "管理员",
    avatar: "A",
  });

  // 移动端支持的下拉菜单状态
  const [dropdownActive, setDropdownActive] = useState(false);

  // 使用导航钩子用于登出跳转
  const navigate = useNavigate();

  // 处理登出
  const handleLogout = async () => {
    const res = await authService.logout();
    if (res.code === 0) {
      message.success("退出登录成功");
      navigate("/login");
    }
  };

  // 点击其他区域关闭下拉菜单
  useEffect(() => {
    const handleClickOutside = (event) => {
      const userInfoEl = document.querySelector(`.${styles.userInfo}`);
      const dropdownEl = document.querySelector(`.${styles.dropdown}`);

      // 确保点击的不是用户信息区域和下拉菜单区域
      if (
        userInfoEl &&
        !userInfoEl.contains(event.target) &&
        // 确保点击事件不是从下拉菜单发出的
        dropdownEl &&
        !dropdownEl.contains(event.target)
      ) {
        setDropdownActive(false);
      }
    };

    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, [styles.userInfo, styles.dropdown]);

  // 切换下拉菜单显示状态（区分菜单项点击和用户信息区域点击）
  const toggleDropdown = (e) => {
    // 阻止事件冒泡，避免立即触发外部点击事件
    e.stopPropagation();

    // 只有点击的是用户信息区域本身才切换菜单状态
    if (e.currentTarget.classList.contains(styles.userInfo)) {
      setDropdownActive(!dropdownActive);
    }
  };

  // 处理菜单项点击，避免自动关闭
  const handleMenuItemClick = (action) => (e) => {
    // 阻止冒泡，避免触发外部点击事件
    e.stopPropagation();
    // 执行指定的动作
    action();
    // 关闭下拉菜单
    setDropdownActive(false);
  };

  // ==================== Runtime ====================
  const BASE_URL = "http://1.203.103.140:8777";
  const PATH = "/api/local_doc_qa/local_doc_chat_new";
  const PATH_STREAM = "/api/local_doc_qa/local_doc_chat_stream";
  let PATH_TEST = "/api/local_doc_qa/stream_test";
  const PATH_PRO = "/api/local_doc_qa/chat_stream";
  // PATH_TEST = PATH_PRO;
  const iframeUrl = "http://127.0.0.1:5500/chart.html";
  // const MODEL = "deepseek-chat";
  // const API_KEY = "sk-5e2effb1d0fb418ca552c813ecb4f43a";
  // const [agent] = useXAgent({
  //   request: async ({ message }, { onSuccess }) => {
  //     onSuccess(`Mock success return. 你问的是: ${message}`);
  //   },
  // });
  const [agent] = useXAgent({
    baseURL: BASE_URL + PATH,
    // model: MODEL,
    // dangerouslyApiKey: API_KEY,
  });
  const [loading, setLoading] = React.useState<boolean>(true);
  const [strRes, setStrRes] = React.useState<any>({});
  async function fetch() {
    const params = JSON.stringify({
      message: "古井贡酒的价位区间是什么",
      messages: ["白酒板块表现如何？"],
    });

    const config = {
      method: "post",
      url: BASE_URL + PATH_STREAM,
      headers: { "Content-Type": "application/json" },
      responseType: "text/event-stream",
      data: params,
    };

    const response = await axios(config);
    // 检查 response.body 是否是 ReadableStream
    if (!(response.body instanceof ReadableStream)) {
      throw new Error("response.body is not a ReadableStream");
    }
    return response;
  }

  const [testMsg, $testMsg] = React.useState<any>([]);
  function fnTest(q) {
    class RetriableError extends Error {}
    class FatalError extends Error {}
    const ctrl = new AbortController();

    setLines([]);
    // 生成唯一对话ID（关键修改）
    const newAiId = Date.now(); // 用时间戳替代 testMsg.length 逻辑
    const copyA = [
      {
        id: testMsg.length + 1,
        loading: false,
        message: q,
        status: "user",
      },
      {
        id: newAiId,
        loading: false,
        message: [],
        status: "ai",
      },
    ];
    $testMsg((prevTestMsg) => {
      const newTestMsg = [...prevTestMsg, ...copyA];
      return newTestMsg;
    });

    // $testMsg([...testMsg, ...copyA]);
    setStrRes({ ...strRes, [newAiId]: [] });
    const params = JSON.stringify({
      message: q,
    });
    fetchEventSource(BASE_URL + PATH_TEST, {
      // fetchEventSource("http://172.16.17.168:8000/test", {
      method: "POST",
      signal: ctrl.signal,
      headers: { "Content-Type": "application/json" },
      openWhenHidden: true,
      body: params,
      async onopen(response) {
        console.log(response);
        // if (!response.body?.locked) {
        //   ctrl.abort();
        // }
        if (
          response.ok &&
          response.headers.get("content-type") === "text/event-stream"
        ) {
          return; // everything's good
        } else if (
          response.status >= 400 &&
          response.status < 500 &&
          response.status !== 429
        ) {
          // client-side errors are usually non-retriable:
          throw new FatalError();
        } else {
          throw new RetriableError();
        }
      },
      onmessage(msg) {
        const id = newAiId;
        setStrRes((prevStrRes) => {
          const updatedStrRes = {
            ...prevStrRes,
            [id]: [...(prevStrRes[id] || []), msg],
          };
          return updatedStrRes;
        });
        // setStrRes({ ...strRes, [id]: [...(strRes[id] || []), msg] });
        // setMessages((pre) => {
        //   return {
        //     ...pre,
        //     [id]: [...(pre[id] || []), message],
        //   };
        // });

        // setLines((pre) => [...pre, { data: msg.data }]);
        if (msg.event === "FatalError") {
          throw new FatalError(msg.data);
        }
      },
      onclose() {
        ctrl.abort();
        // if the server closes the connection unexpectedly, retry:
        throw new RetriableError();
      },
      onerror(err) {
        ctrl.abort();
        if (err instanceof FatalError) {
          throw err; // rethrow to stop the operation
        } else {
          // do nothing to automatically retry. You can also
          // return a specific retry interval here.
        }
      },
    });
  }
  async function request(q: string) {
    setLoading(false);
    const copyA = [...messages];
    copyA.push({
      id: copyA.length,
      loading: false,
      message: q,
      status: "user",
    });

    setMessages(copyA);
    agent.request(
      {
        message: q,
        // messages: [{ role: "user", content: "hello, who are u?" }],
        // stream: true,
      },
      {
        onSuccess: (messages) => {
          setLoading(true);
          if (
            Array.isArray(messages) &&
            messages.length > 0 &&
            messages[0].history
          ) {
            const arr = messages[0].history[0];
            let res = arr.map((el: any, idx: number) => {
              return {
                id: idx,
                loading: true,
                message: el,
                status: idx % 2 == 0 ? "user" : "ai",
              };
            });
            setMessages(res);
          }
        },
        onError: (error) => {
          console.error("onError", error);
        },
        onUpdate: (msg) => {
          console.log("onUpdate", msg);
        },
      }
    );
  }

  const { onRequest, messages, setMessages } = useXChat({
    agent,
  });

  useEffect(() => {
    if (activeKey !== undefined) {
      setMessages([]);
    }
  }, [activeKey]);

  // ==================== Event ====================
  const onSubmit = (nextContent: string) => {
    if (!nextContent) return;
    // onRequest(nextContent);
    // request(nextContent);
    fnTest(nextContent);
    setContent("");
  };

  const onPromptsItemClick: GetProp<typeof Prompts, "onItemClick"> = (info) => {
    // request(info.data.description as string);
    fnTest(info.data.description as string);
  };

  const onAddConversation = () => {
    setConversationsItems([
      ...conversationsItems,
      {
        key: `${conversationsItems.length}`,
        // label: `新会话 ${conversationsItems.length}`,
        label: `新会话`,
      },
    ]);
    setActiveKey(`${conversationsItems.length}`);
  };

  const onConversationClick: GetProp<typeof Conversations, "onActiveChange"> = (
    key
  ) => {
    setActiveKey(key);
  };

  const handleFileChange: GetProp<typeof Attachments, "onChange"> = (info) =>
    setAttachedFiles(info.fileList);

  // ==================== Nodes ====================
  const placeholderNode = (
    <Space direction="vertical" size={16} className={styles.placeholder}>
      <Welcome
        variant="borderless"
        icon="https://mdn.alipayobjects.com/huamei_iwk9zp/afts/img/A*s5sNRo5LjfQAAAAAAAAAAAAADgCCAQ/fmt.webp"
        title="Hello, 我是华创证券AI金融助手"
        description="基于蚂蚁设计，AGI产品界面解决方案，打造更好的智能视觉~"
      />
      <Prompts
        title="猜你想提问"
        items={placeholderPromptsItems}
        styles={{
          list: {
            width: "100%",
          },
          item: {
            flex: 1,
          },
        }}
        onItemClick={onPromptsItemClick}
      />
    </Space>
  );

  const items: GetProp<typeof Bubble.List, "items"> = messages.map(
    ({ id, message, status }) => {
      return {
        key: id,
        loading: status === "loading",
        role: status === "local" ? "local" : "ai",
        content: message,
      };
    }
  );

  const attachmentsNode = (
    <Badge dot={attachedFiles.length > 0 && !headerOpen}>
      <Button
        type="text"
        icon={<PaperClipOutlined />}
        onClick={() => setHeaderOpen(!headerOpen)}
      />
    </Badge>
  );

  const senderHeader = (
    <Sender.Header
      title="附件"
      open={headerOpen}
      onOpenChange={setHeaderOpen}
      styles={{
        content: {
          padding: 0,
        },
      }}
    >
      <Attachments
        beforeUpload={() => false}
        items={attachedFiles}
        onChange={handleFileChange}
        placeholder={(type) =>
          type === "drop"
            ? { title: "将文件放到此处" }
            : {
                icon: <CloudUploadOutlined />,
                title: "上传文件",
                description: "单击或拖动文件到此区域进行上传",
              }
        }
      />
    </Sender.Header>
  );

  const logoNode = (
    <div className={styles.logo}>
      <img
        src="https://www.hczq.com/favicon.ico"
        draggable={false}
        alt="logo"
      />
      <span>华创证券AI金融助手</span>
    </div>
  );

  function handleFileItemClick(el: any) {
    console.log("handleFileItemClick", el);
    setShowPdf(true);
  }
  const scrollContainerRef = useRef<HTMLDivElement>(null);
  useEffect(() => {
    // 内容变化时滚动到最底部
    if (scrollContainerRef.current) {
      scrollContainerRef.current.scrollTop =
        scrollContainerRef.current.scrollHeight;
    }
  }, [testMsg, strRes]);
  useEffect(() => {
    const userInfo = getSession("userInfo");
    setUserInfo(userInfo);
    const handleClick = (event: any) => {
      const fileItem = event.target.closest(".file-item");
      if (fileItem) {
        const el = JSON.parse(fileItem.getAttribute("data-el"));
        handleFileItemClick(el);
      }
    };

    document.addEventListener("click", handleClick);

    return () => {
      document.removeEventListener("click", handleClick);
    };
  }, []);

  // 处理菜单收起展开
  const handleToggleCollapse = () => {
    setMenuCollapsed(!menuCollapsed);
  };

  // 添加用户信息组件
  const userInfoNode = (
    <div
      className={`${styles.userInfo} ${dropdownActive ? styles.active : ""}`}
      onClick={toggleDropdown}
    >
      <div className={styles.avatar}>{`User`}</div>
      <span className={styles.username}>{userInfo.phone}</span>
      <CaretDownOutlined
        style={{ fontSize: 12, color: "rgba(0, 0, 0, 0.45)" }}
      />

      {/* 下拉菜单 */}
      <div className={styles.dropdown}>
        <div
          className={styles.item}
          onClick={handleMenuItemClick(() => message.info("功能开发中"))}
        >
          <SettingOutlined className={styles.icon} />
          <span>设置</span>
        </div>
        <div
          className={styles.item}
          onClick={handleMenuItemClick(handleLogout)}
        >
          <LogoutOutlined className={styles.icon} />
          <span>退出登录</span>
        </div>
      </div>
    </div>
  );

  // ==================== Render =================
  return (
    <div className={styles.layout}>
      <div
        className={`${styles.menu} ${menuCollapsed ? styles.collapsed : ""}`}
      >
        {/* 🌟 Logo */}
        {logoNode}

        {/* 🌟 会话管理 */}
        <Conversations
          items={conversationsItems}
          className={styles.conversations}
          activeKey={activeKey}
          onActiveChange={onConversationClick}
        />

        {/* 🌟 添加会话 */}
        <Button
          onClick={onAddConversation}
          type="link"
          className={styles.addBtn}
          icon={<PlusOutlined />}
        >
          <span className={styles.btnText}>新建会话</span>
        </Button>

        {/* 收起/展开按钮 */}
        <div
          className={`${styles.collapseBtn} ${
            menuCollapsed ? styles.collapsed : ""
          }`}
          onClick={handleToggleCollapse}
        >
          {menuCollapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
        </div>
      </div>
      <div className={styles.layoutMain}>
        {/* 添加顶栏 */}
        <div className={styles.header}>{userInfoNode}</div>

        <div
          className={`${styles.mainContent} ${
            !showPdf ? styles.withoutPdf : ""
          }`}
        >
          <div className={styles.chat}>
            <div className={styles.chatCon} ref={scrollContainerRef}>
              {testMsg.length > 0
                ? testMsg.map((el: any, idx: number) => {
                    return (
                      <Bubble
                        key={el.id}
                        variant="outlined"
                        loading={
                          el.status == "user"
                            ? false
                            : strRes[el.id].length > 0
                            ? false
                            : true
                        }
                        // typing={{ suffix: <>💗</> }}
                        placement={el.status == "user" ? "end" : "start"}
                        avatar={
                          el.status == "ai"
                            ? {
                                icon: <OpenAIOutlined />,
                                style: { background: "#fde3cf" },
                              }
                            : {
                                icon: <UserOutlined />,
                                style: { background: "#87d068" },
                              }
                        }
                        messageRender={renderMarkdown}
                        content={
                          el.status == "user"
                            ? el.message
                            : strRes[el.id]
                                .map((line, idx) => {
                                  const res = JSON.parse(line.data);
                                  // 思考区域
                                  if (idx > 0) {
                                    const preRes = JSON.parse(
                                      strRes[el.id][idx - 1].data
                                    );
                                    if (
                                      preRes.id !== "_think" &&
                                      res.id == "_think"
                                    ) {
                                      res.content =
                                        '<div class="think-con">' + res.content;
                                    }
                                  }
                                  if (idx == 0 && res.id == "_think") {
                                    res.content =
                                      '<div class="think-con">' + res.content;
                                  }

                                  // 正文区域
                                  if (strRes[el.id][idx + 1]) {
                                    const nextRes = JSON.parse(
                                      strRes[el.id][idx + 1].data
                                    );
                                    if (
                                      nextRes.id !== "_think" &&
                                      res.id == "_think"
                                    ) {
                                      res.content =
                                        res.content +
                                        "</div><div class='answer-con'>";
                                    }
                                    if (
                                      nextRes.id !== "_cot" &&
                                      res.id == "_cot"
                                    ) {
                                      res.content = res.content + "</div>";
                                    }
                                  }
                                  if (res.id == "answer_source") {
                                    const title_str =
                                      "<span class='title'>参考文档</span>";
                                    let file_con = res.content.final_source
                                      .map((el: any, idx: number) => {
                                        return `<div class="file-item" data-el='${JSON.stringify(
                                          el
                                        )}'><div class="file-t"><span class="tag">${
                                          idx + 1
                                        }</span><span class="text">${
                                          el.text
                                        }</span></div><div class="file-b"><span class="title" title="${
                                          el.title
                                        }">${
                                          el.title
                                        }</span><span class="publish-date">${
                                          el.publish_date
                                        }</span></div></div>`;
                                      })
                                      .join("");
                                    file_con =
                                      '<div class="file-list">' +
                                      file_con +
                                      "</div>";
                                    // const res_con = `<div><div class="source-con">${title_str}${file_con}</div> <iframe style="border:none;margin:5px 0;width:100%;height:300px" src="${iframeUrl}" /></div>`;
                                    const res_con = `<div class="source-con">${title_str}${file_con}</div>`;
                                    return res_con;
                                  }
                                  return res.content;
                                  // return "sdd";
                                })
                                .join("")
                        }
                      />
                    );
                  })
                : placeholderNode}
            </div>
            <Prompts
              items={senderPromptsItems}
              onItemClick={onPromptsItemClick}
            />
            <Sender
              value={content}
              header={senderHeader}
              onSubmit={onSubmit}
              onChange={setContent}
              prefix={attachmentsNode}
              loading={agent.isRequesting()}
              className={styles.sender}
            />
          </div>
          {showPdf && (
            <div className={styles.pdfPreview}>
              <Button
                type="text"
                icon={<CloseOutlined />}
                className={styles.closeBtn}
                onClick={() => setShowPdf(false)}
              />
              <PdfOreviewer />
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

export default ChatPage;
